//============================================================================
// Name        : bitmap.c
// Author      : Yugi
// Version     : 1.0
// Description : 
//============================================================================

#include <string.h>

#include "bitmap.h"

/* 
* Generate a simple bitmap
* 
* REQUIRE:	bits greater than 0
* RETURN:	new bitmap
* NOTES:	Return value should be freed by user
*/
PSBITMAP bm_new(unsigned int bits){
	PSBITMAP pBitmap = (PSBITMAP) malloc(sizeof(SBITMAP));

	/* Init */
	pBitmap->size = bits/8 + ((bits%8)>0 ? 1 : 0);
	pBitmap->data = (char *) malloc(pBitmap->size);
	memset(pBitmap->data, 0, pBitmap->size);
	return pBitmap;
}

/* 
* Reutrns a clone of the input bitmap
*
* REQUIRE:	pSrc not NULL
* RETURN:	a clone of pSrc
* NOTES:	Return value should be freed by user
*/
PSBITMAP bm_clone(PSBITMAP pSrc){
	PSBITMAP pBitmap = (PSBITMAP) malloc(sizeof(SBITMAP));

	pBitmap->size = pSrc->size;
	pBitmap->data = (char *) malloc(pBitmap->size);

	/* Copy data */
	strncpy(pBitmap->data, pSrc->data, pBitmap->size);
	return pBitmap;
}

/* 
* Merge bitmap
*
* REQUIRE:	bitmap1 not NULL
*			bitmap2 not NULL
* RETURN:	bitmap merged from bitmap1 and bitmap2
* NOTES:	Return value should be freed by user
*/
PSBITMAP bm_merge(PSBITMAP bitmap1, PSBITMAP bitmap2){
	PSBITMAP pBitmap = (PSBITMAP) malloc(sizeof(SBITMAP));
	unsigned int index;

	if (bitmap1->size != bitmap2->size)
		return NULL;

	pBitmap->data = (char *) malloc(bitmap1->size);

	for (index=0; index<bitmap1->size; index++)
		pBitmap->data[index] = bitmap1->data[index] | bitmap2->data[index];

	return pBitmap;
}

/* 
* Free the memory hold by bitmap instance.
*
* REQUIRE:	pBitmap not NULL
* MODIFY:	pBitmap
*/
void bm_delete(PSBITMAP pBitmap){\
	/* First free the data */
	if (pBitmap->data != NULL){
		free(pBitmap->data);
	}
	
	/* Then free the bitmap */
	free(pBitmap);
	pBitmap = NULL;
}

/* 
* Test if pBitmap1 and pBitmap2 are indistinguishable.
*
* REQUIRE:	pBitmap1, pBitmap2 not NULL, their data not NULL
* RETURN:	true if pBitmap1 and pBitmap2 are indistinguishable
*			false otherwise
*/
bool bm_equals(PSBITMAP pBitmap1, PSBITMAP pBitmap2){
	/* sizes should be same */
	if (pBitmap1->size != pBitmap2->size)
		return false;

	/* finally, we compare them */
	if (strncmp(pBitmap1->data, pBitmap2->data, pBitmap1->size))
		return false;

	return true;
}

/* 
* Set bit. 
*
* REQUIRE:	pBitmap not NULL, index / 8 less than pBitmap->size
* MODIFY:	pBitmap
*/
void bm_setbit(PSBITMAP pBitmap, unsigned int index){
	unsigned int base = index / 8;
	unsigned int offset = index % 8;

	if (base < pBitmap->size)
		pBitmap->data[base] = pBitmap->data[base] | (1 << offset);
}

/* 
* Clear bit.
*
* REQUIRE:	pBitmap not NULL, index / 8 less than pBitmap->size
* MODIFY:	pBitmap
*/
void bm_clearbit(PSBITMAP pBitmap, unsigned int index){
	unsigned int base = index / 8;
	unsigned int offset = index % 8;

	if (base < pBitmap->size)
		pBitmap->data[base] = pBitmap->data[base] | ~(1 << offset);
}

/* 
* Test bit.
*
* REQUIRE:	pBitmap not NULL, index / 8 less than pBitmap->size
* MODIFY:	pBitmap
*/
bool bm_testbit(PSBITMAP pBitmap, unsigned int index){
	unsigned int base = index / 8;
	unsigned int offset = index % 8;

	if (base < pBitmap->size)
		return !!(pBitmap->data[base] & (1 << offset));

	return false;
}

/* Print a bitmap */
void bm_print(PSBITMAP pBitmap, file_t outfile){
	unsigned int index = 0;

	dr_fprintf(outfile, "%.8x", *((int*)pBitmap->data));
	dr_fprintf(outfile, "-----Bitmap-----");

	while (index < pBitmap-> size*8){
		if ((index % 16) == 0)
			dr_fprintf(outfile, "\n");
		
		dr_fprintf(outfile, "%x", bm_testbit(pBitmap, index));
		index++;
	}

	dr_fprintf(outfile, "\n");
}